package com.hp.bon.sgw.service;

import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.bon.sgw.Constants;
import com.hp.bon.sgw.bean.CapabilityLimitCfgBean;
import com.hp.bon.sgw.bean.FluxSumSgwViewBean;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.domain.Node;
import com.hp.bon.sgw.domain.OpenedCapability;
import com.hp.bon.sgw.snmp.SgwNMContext;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.util.Comutils;

/**
 * 流量控制服务，计算流量，确定是否断连
 * 
 * @author yudan
 * 
 */
public class FluxControlSgwViewService implements ConfigListener {

	private static FluxControlSgwViewService instance;

	public static FluxControlSgwViewService getInstance() {
		if (instance == null) {
			instance = new FluxControlSgwViewService();
			instance.init();

			SpringContext.getInstance().getConfigService().addConfigListener(instance);
		}
		return instance;
	}

	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(FluxControlSgwViewService.class);

	public FluxControlSgwViewService() {
		
	}

	/**
	 * 记录流量，key是capability+nehost+user
	 */
	private ConcurrentHashMap<String, FluxSumSgwViewBean> fluxSumMap = new ConcurrentHashMap<String, FluxSumSgwViewBean>();
	/**
	 * 因为流量超标而被临时禁止的网元，key是网元hostID,value是禁止截至期限（时间戳）
	 */
	private volatile Map<String, Long> denniedNEMap = Collections.emptyMap();

	public Map<String, Long> getDenniedNEMap() {
		return denniedNEMap;
	}

	public boolean checkAllowedConnect(String neHost, String serviceContextId) {
		String key = this.getMapKey(neHost, serviceContextId);
		Long denniedTime = denniedNEMap.get(key);
		if (denniedTime == null) {
			return true;
		} else {
			// System.out.println(Comutils.formatTime(denniedTime,
			// "yyyy-MM-dd HH:mm:ss.SSS"));
			if (denniedTime < System.currentTimeMillis()) {
				// 已经过期
				return true;
			} else {
				return false;
			}
		}
	}

	public void init() {
		;
	}

	public void doPeriodWork() {
		checkAndRestFluxBean();
	}

	/**
	 * 检查用户流量是否超标
	 * 
	 * @param capability
	 * @param data
	 *            .getNeHost()
	 * @param user
	 * @param msgLength
	 * @return
	 */
	public int checkUserFlux(CallParam callParam) {
		int retCode = 0;// 成功
		Node node = callParam.getFromNode();
		String neHost = node.getHostId();
		String serviceContextId = callParam.getServiceContextId();
		String limitKey = this.getMapKey(neHost, serviceContextId);
		if (!this.checkAllowedConnect(neHost, serviceContextId)) {
			logger.warn(Constants.ResultCode.FLUX_DISCONNECT + "|" + neHost + " has been dennied,please wait some more minutes ");
			return Constants.ResultCode.FLUX_DISCONNECT;
		}

		CapabilityLimitCfgBean limitConfBean = this.getLimitConfBean(neHost, serviceContextId);
		
		String reason = "";
		long totalReq = 0;
		if( limitConfBean != null && limitConfBean.getLimitType() == OpenedCapability.CONTROL_BY_COUNT) {
			FluxSumSgwViewBean fluxBean = findFluxSumSgwViewBean(limitKey);
			fluxBean.increAccess();
			totalReq = fluxBean.getTotalRequests();
			
//			long deniniedSend = limitConfBean.getRecTime();
			
			if (totalReq > limitConfBean.getLimitDiscount()) {
				retCode = Constants.ResultCode.FLUX_DISCONNECT;
//				putToDenined(neHost, serviceContextId,  deniniedSend);
				reason = "count exceed disconnect threshold values";
			} else if (totalReq > limitConfBean.getLimitWarning()) {
				retCode = Constants.ResultCode.FLUX_WARNING;
				reason = "count exceed warning threshold values";
			}
		}
		
		if (retCode != 0) {
			if (retCode == Constants.ResultCode.FLUX_DISCONNECT || (retCode == Constants.ResultCode.FLUX_WARNING && (totalReq - callParam.getCapabilityBean().getCountWarning() == 1))) {
				logger.warn(Constants.SYSLOG_FluxContrlWarn + "|" + retCode + "|" + node.getNodeCode() + "|" + node.getHostId() + "|" + callParam.getCapability() + "|" + totalReq + "|" + 0 + "|" + node.getHostId() + " " + reason);
				SgwNMContext.notifyFlowSizeAlert(node.getNodeCode(), node.getHostId(), callParam.getCapability(), totalReq, 0, reason, System.currentTimeMillis() / 1000);
			} else {
				logger.warn(retCode + "|" + node.getHostId() + " request " + reason);
			}
		}
		return retCode;
	}
	
	public String getMapKey(String hostId, String serviceContextId){
		return String.format("%s<%s>", hostId, serviceContextId);
	}

	private void putToDenined(String neHostID, String serviceContextId, long denniedPeriod) {
		String key = getMapKey(neHostID, serviceContextId);
		Long time = denniedNEMap.get(key);
		Long newTime = System.currentTimeMillis() + denniedPeriod * 1000;
		if (time != null) {
			denniedNEMap.put(key, newTime);
		} else {
			// 复制一份新的Map，并加入新限制的主机
			Map<String, Long> newMap = new LinkedHashMap<String, Long>(denniedNEMap);
			newMap.put(key, newTime);
			this.denniedNEMap = newMap;
		}
		logger.info("denny " + neHostID + " until " + Comutils.formatTime(newTime, "yyyy-MM-dd HH:mm:ss.SSS"));
	}

	private FluxSumSgwViewBean findFluxSumSgwViewBean(String key) {
		FluxSumSgwViewBean fluxBean = fluxSumMap.get(key);
		if (fluxBean == null) {
			ConcurrentHashMap<String, FluxSumSgwViewBean> newFluxSumMap = new ConcurrentHashMap<String, FluxSumSgwViewBean>(fluxSumMap);
			fluxBean = new FluxSumSgwViewBean();
			newFluxSumMap.put(key, fluxBean);
			this.fluxSumMap = newFluxSumMap;
		}
		return fluxBean;
	}

	/**
	 * 周期性检查流控数据，进行清理
	 */
	private void checkAndRestFluxBean() {
		//logger.info("----------checkAndRestFluxBean");
		Map<String, CapabilityLimitCfgBean> crlBeanMap = SpringContext.getInstance().getConfigService().getCapabilityLimitCfgMap();
		for (Map.Entry<String, FluxSumSgwViewBean> entry : this.fluxSumMap.entrySet()) {
			CapabilityLimitCfgBean ctrlBean = crlBeanMap.get(entry.getKey());
			if (ctrlBean != null && ctrlBean.getLimitType() != OpenedCapability.CONTROL_NONE) {
				FluxSumSgwViewBean fluBean = entry.getValue();
				//logger.info("----------checkAndRestFluxBean, : getTotalRequests=" + fluBean.getTotalRequests());
				if (fluBean.getResetTime() + ctrlBean.getLimitCycleSecond() * 1000 < System.currentTimeMillis()) {
					fluBean.reset();
				}
			}
		}
	}

	@Override
	public Set<String> monitorConfKeys() {
		return new HashSet<String>();
	}

	@Override
	public void onConfigUpdated(String updatedKey) {
		;
	}
	
	public CapabilityLimitCfgBean getLimitConfBean(String hostId, String serviceContextId){
		String key = this.getMapKey(hostId, serviceContextId);
		return SpringContext.getInstance().getConfigService().getCapabilityLimitCfgMap().get(key);
	}

}
